#ifndef SPECTRUM_STATIC_H
#define SPECTRUM_STATIC_H

#include <string>
#include <math.h>
#include <vector>

namespace gemren
{
	template<int N>
	struct spectrum_static
	{
		static const float infinity;
		
		typedef spectrum_static<N> spectrum;
		static spectrum pow(const spectrum& s, const float p)
		{
			spectrum ret(s);
			for(int i = 0; i <= s.len; ++i)
				ret.vals[i]= std::pow(ret.vals[i], p);
			return ret;
		}
		spectrum_static():len(0) {}
		spectrum_static(const spectrum& s): len(s.len)
		{
			std::copy_n(s.vals, len+1, vals);
		}
		spectrum_static(const std::string& value, float min, float max)
		{
			std::vector<float> vvals;
			std::stringstream ss(value);
			while(!ss.eof())
			{
				float val;
				ss>>val;
				if(ss.fail())
					break;
				vvals.push_back(embree::clamp(val, min, max));
			}
			if(vvals.size() == 0)
			{
				len = 0;
				vals[0] = 0.0f;
			}
			if(vvals.size()<=N)
			{
				std::copy(vvals.begin(), vvals.end(), vals);
				len = int(vvals.size() - 1);
			}
			else
			{
				std::copy_n(vvals.begin(), N, vals);
				len = N-1;
			}
			
		}
		~spectrum_static(){}
		bool is_const()const{ return len==0; }
		spectrum_static& operator=(const spectrum& s)
		{
			len = s.len;
			std::copy(s.vals, s.vals + len+1, vals);
			return *this;
		}
		template<typename Iter>
		spectrum_static(const Iter& begin, const Iter& end)
			:len(int(end-begin-1))
		{
			if(len<0)
			{
				len = 0;
				vals[0] = 0.0f;
			}
			else
				std::copy(begin, end, vals);
		}
		spectrum_static(float val): len(0) { vals[0] = val; }
		float operator[](float wavelength) const
		{
			return at(wavelength);
		}
		inline float at(float wavelength) const
		{
			//assume 0 <= wl <= 1
			wavelength *= len;
			int left = int(wavelength), right = int(ceil(wavelength));
			float weight = fmod(wavelength, 1.0f);
			return (1.0f-weight)*vals[left] + weight*vals[right];
		}
		friend class color_converter;

		std::string to_string()
		{
			std::stringstream ss;
			for(int i = 0; i <= len; ++i)
				ss<<vals[i]<<" ";
			return ss.str();
		}
		size_t interval_count()const { return len; }
		
		spectrum_static<N>& operator *= (const float f)
		{
			for(int i = 0; i<=len; ++i)
			{
				vals[i] *= f;
			}
			return *this;
		}
		spectrum_static<N>& operator *= (const spectrum_static<N>& s)
		{
			if(s.len == 0)
				for(int i = 0; i <= len; ++i)
					vals[i] *= s.vals[0];
			else
			{
				float buf[N];
				int newsize = bounded_lcm(s.len+1, len+1)-1;
			
				for(int i = 0; i<=newsize; ++i)
				{
					float ind = float(i)/newsize;
					buf[i] = s[ind]* (*this)[ind];
				}
				len = newsize;
				std::copy_n(buf, len+1, vals);
			}
			return *this;
			
		}
		spectrum_static<N>& operator += (const spectrum_static<N>& s)
		{
			if(s.len == 0)
				for(int i = 0; i <= len; ++i)
					vals[i] += s.vals[0];
			else
			{
				float buf[N];
				int newsize = bounded_lcm(s.len+1, len+1)-1;
				for(int i = 0; i<=newsize; ++i)
				{
					float ind = float(i)/newsize;
					buf[i] = s[ind]+ (*this)[ind];
				}
				len = newsize;
				std::copy_n(buf, len+1, vals);
			}
			return *this;
		}
		float max() const
		{
			float m = vals[0];
			for(int i = 1; i <= len; ++i)
				if(vals[i] > m) m = vals[i];
			return m;
		}
		float min() const
		{
			float m = vals[0];
			for(int i = 1; i <= len; ++i)
				if(vals[i] < m) m = vals[i];
			return m;
		}
		float integral() const
		{
			float ret = 0;
			for(int i = 0; i <= len; ++i)
				ret += vals[i];
			return ret/(len+1);
		}
		int getl() const { return len; }
		float getv(int i) const { return vals[i]; } 
	protected:
		//lowest common denominator, N at most
		inline int bounded_lcm(int a, int b)
		{
			//compute gcd
			int ab[] = {a,b};
			int cur = 0;
			while(ab[cur])
			{
				ab[1-cur] = ab[1-cur] % ab[cur];
				cur = 1-cur;
			}
			//lcm = a*b/gcd
			return std::min(N, a*b/ab[1-cur]);
		}

		int len; //number of intervals = # of vals - 1
		float vals[N];
	};

}

#endif